package com.dferreira.languagesteach.helpers;

import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Handler;
import android.view.View;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.ViewFlipper;

import com.dferreira.languagesteach.data_adapters.WorkoutAdapter;
import com.dferreira.languagesteach.meta_data.IntroductionState;
import com.dferreira.languagesteach.utils.AudioDelegator;
import com.dferreira.languagesteach.utils.DeviceStretcher;
import com.dferreira.languagesteach.utils.SlideAnimator;

import java.io.File;

public class IntroductionDelegator implements Runnable {

    private final ViewFlipper viewFlipper;
    private final TextView[] imagesDescription;
    private final TextView imageCounter;
    private final ImageButton againBtn;
    private final ImageButton backBtn;
    private final ImageButton playAndPauseBtn;
    private final ImageButton nextBtn;
    private final WorkoutAdapter workoutAdapter;
    private final LinearLayout playerPanel;

    private IntroductionState state;

    /* Handlers */
    private final AudioDelegator audioDelegator;
    private final SlideAnimator slideAnimator;
    private final Handler mHandler;
    private final Activity activity;
    private Boolean withErrors;
    private final DeviceStretcher deviceStretcher;

    /**
     * @param context             global information about an application environment.
     * @param viewFlipper         view with children that allow swipe between elements
     * @param imageDescription1   image and description from the first child of flipper view
     * @param imageDescription2   image and description from the second child of flipper view
     * @param imageCounter        will show the counter of the images
     * @param playerPanel         panel with the buttons to control the images
     * @param againBtn            reload image button
     * @param backBtn             go back button reference
     * @param playAndPauseBtn     play or pause button
     * @param nextBtn             forward button
     * @param activity            reference to the current activity
     * @param workoutAdapter   required information about the current subject
     * @param waitBeforeNextSlide time from the delay between the end of a sound and the start reproduction from the next one
     */
    public IntroductionDelegator(Context context,
                                 ViewFlipper viewFlipper, TextView imageDescription1,
                                 TextView imageDescription2,
                                 TextView imageCounter,
                                 LinearLayout playerPanel, ImageButton againBtn,
                                 ImageButton backBtn, ImageButton playAndPauseBtn,
                                 ImageButton nextBtn, Activity activity,
                                 WorkoutAdapter workoutAdapter, Integer waitBeforeNextSlide) {

        this.imagesDescription = new TextView[2];

        this.viewFlipper = viewFlipper;
        this.imagesDescription[0] = imageDescription1;
        this.imagesDescription[1] = imageDescription2;

        // Action buttons
        this.playerPanel = playerPanel;
        this.againBtn = againBtn;
        this.backBtn = backBtn;
        this.playAndPauseBtn = playAndPauseBtn;
        this.nextBtn = nextBtn;
        this.workoutAdapter = workoutAdapter;
        this.imageCounter = imageCounter;

        // Initialize handlers
        this.mHandler = new Handler();
        this.audioDelegator = new AudioDelegator(mHandler, this, waitBeforeNextSlide);
        this.activity = activity;

        this.slideAnimator = new SlideAnimator(context, viewFlipper);
        this.withErrors = false;
        this.deviceStretcher = new DeviceStretcher(context);
    }

    /**
     * Enables or disables the icons of buttons
     */
    private void refreshButtonsIcons() {
        boolean isFirstSlide;
        boolean isLastSlide;

        isFirstSlide = (state.getCurrentFigure() == 0);
        isLastSlide = (state.getCurrentFigure() + 1) == workoutAdapter
                .getNumberOfEntries();

        if (state.getPaused()) {
            playAndPauseBtn.setImageResource(android.R.drawable.ic_media_play);
            againBtn.setEnabled(false);
        } else {
            playAndPauseBtn.setImageResource(android.R.drawable.ic_media_pause);
            againBtn.setEnabled(true);
        }

        if (isFirstSlide && backBtn.isEnabled()) {
            backBtn.setEnabled(false);
        } else {
            if ((!isFirstSlide) && (!backBtn.isEnabled())) {
                backBtn.setEnabled(true);
            }
        }

        if (isLastSlide && nextBtn.isEnabled()) {
            nextBtn.setEnabled(false);
        } else {
            if ((!isLastSlide) && !nextBtn.isEnabled()) {
                nextBtn.setEnabled(true);
            }
        }
    }

    /**
     * Refresh the image and his description
     */
    private void setImageAndDescription() {
        Drawable imageView;
        imageView = workoutAdapter.getImageView(state.getCurrentFigure());
        if (imageView == null) {
            reportAnError();
            return;
        }
        imageCounter.setText((state.getCurrentFigure() + 1) + "/"
                + workoutAdapter.getNumberOfEntries());
        imagesDescription[state.getCurrentFigure() & 1]
                .setText(workoutAdapter.getImageDescription(state
                        .getCurrentFigure()));
        imagesDescription[state.getCurrentFigure() & 1].setCompoundDrawablesWithIntrinsicBounds(null, null, null, deviceStretcher.zoomDrawable(imageView));
    }

    /**
     * method called when something wrong was detected
     */
    private void reportAnError() {
        withErrors = true;
        activity.onBackPressed();
    }

    /**
     * Print in screen and launch the sound output to some picture
     */
    private void doASingleStep() {
        String audioPath;
        boolean readAudio;
        audioPath = workoutAdapter.getAudioDescriptor(state
                .getCurrentFigure());

        refreshButtonsIcons();
        setImageAndDescription();
        readAudio = audioDelegator.playPath(audioPath);
        if (!readAudio) {
            if (!(new File(audioPath).exists())) {
                reportAnError();
            }
        }
    }


    /**
     * If is running without user interactions will show the slides one by one
     * in an presentation fashion way.
     */
    public void run() {
        boolean isLastSlide;

        isLastSlide = (state.getCurrentFigure() + 1) == workoutAdapter
                .getNumberOfEntries();
        try {
            state.getLookHumanInteraction().acquire();
            if (state.getLastHumanInteractionTimestamp() < state
                    .getScheduleTimestamp()) {
                if ((!state.getPaused()) && (!isLastSlide)) {
                    state.setCurrentFigure(state.getCurrentFigure() + 1);
                    doASingleStep();
                    slideAnimator.setAnimationToNextSlide();
                    viewFlipper.showNext();
                } else {
                    // Last element
                    if ((!state.getPaused()) && (isLastSlide)) {
                        stop();
                    }
                }
            }
            state.getLookHumanInteraction().release();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * Called when the activity lifecycle is resumed
     *
     * @param state last introduction state that allows to show resume the action
     */
    public void onResume(IntroductionState state) {
        this.state = state;
        this.audioDelegator.setUnitInteraction(state);

        if (!state.getPaused()) {
            audioDelegator.setScheduleMoreEvents(true);
            doASingleStep();
        }
    }

    /**
     * called when the activity is going to background
     */
    public void onPause() {
        try {
            state.getLookHumanInteraction().acquire();
            state.setLastHumanInteractionTimestamp();
            if (!state.getPaused()) {
                audioDelegator.setScheduleMoreEvents(false);
                audioDelegator.stopAudio();
                mHandler.removeCallbacks(this);
            }
            state.getLookHumanInteraction().release();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * Control reload button behavior
     */
    public void reload() {
        try {
            state.getLookHumanInteraction().acquire();
            state.setLastHumanInteractionTimestamp();
            if (!state.getPaused()) {
                mHandler.removeCallbacks(this);
                audioDelegator.replayLastAsset();
            }
            state.getLookHumanInteraction().release();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * Control previous button behavior
     */
    public void previous() {
        boolean onFirstSlide;

        onFirstSlide = (state.getCurrentFigure() == 0);
        try {
            state.getLookHumanInteraction().acquire();
            if (!onFirstSlide) {
                state.setLastHumanInteractionTimestamp();
                if (!state.getPaused()) {
                    mHandler.removeCallbacks(this);
                    audioDelegator.stopAudio();
                }
                state.setCurrentFigure(state.getCurrentFigure() - 1);
                doASingleStep();
                slideAnimator.setAnimationToPreviousSlide();
                viewFlipper.showPrevious();
            }
            state.getLookHumanInteraction().release();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * Control play/pause button behavior
     */
    public void playAndPause() {
        try {
            state.getLookHumanInteraction().acquire();
            state.setLastHumanInteractionTimestamp();
            if (state.getPaused()) {
                state.setPaused(false);
                doASingleStep();
            } else {
                state.setPaused(true);
                mHandler.removeCallbacks(this);
                audioDelegator.stopAudio();
                refreshButtonsIcons();
            }
            state.getLookHumanInteraction().release();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * Go to the next picture if there is in last slide jumps out.
     * if is paused increments the counter a add one to the currentFigure
     * otherwise looks the human action semaphore add the counter and add one to
     * currentFigure, executes the step and releases the humanAction
     */
    public void forward() {
        boolean isLastSlide;
        isLastSlide = (state.getCurrentFigure() + 1) == workoutAdapter
                .getNumberOfEntries();
        try {
            state.getLookHumanInteraction().acquire();
            if (!isLastSlide) {
                state.setLastHumanInteractionTimestamp();
                if (!state.getPaused()) {
                    mHandler.removeCallbacks(this);
                    audioDelegator.stopAudio();
                }
                state.setCurrentFigure(state.getCurrentFigure() + 1);
                doASingleStep();
                slideAnimator.setAnimationToNextSlide();
                viewFlipper.showNext();
            }
            state.getLookHumanInteraction().release();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * stops immediately the current activity
     */
    public void stop() {
        viewFlipper.startAnimation(slideAnimator.getOutToUp());
        activity.onBackPressed();
    }

    /**
     * Hide options available on player menu
     */
    private void hidePlayerMenu() {
        if (playerPanel.getVisibility() != View.GONE) {
            playerPanel.setVisibility(View.GONE);
        }
    }

    /**
     * Makes the control menu appear smoothly
     */
    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    private void makesControlMenuAppearsSmoothly() {
        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.HONEYCOMB) {
            float yTranslationBackup = playerPanel.getTranslationY();
            playerPanel.setTranslationY(playerPanel.getHeight());
            playerPanel.setTranslationY(yTranslationBackup);
        }
    }

    /**
     * set the panel as visible and makes him appear
     */
    private void showPlayerMenu() {
        playerPanel.setVisibility(View.VISIBLE);
        makesControlMenuAppearsSmoothly();
    }

    /**
     * If the menu is visible hide if is hidden show
     */
    public void showAndHidePlayerMenu() {
        if (playerPanel.getVisibility() == View.GONE) {
            showPlayerMenu();
        } else {
            hidePlayerMenu();
        }
    }

    /**
     * @return if has any errors
     */
    public Boolean getWithErrors() {
        return withErrors;
    }

}
